#include "s2a.h"
#include "mysql.h"
#include "network.h"

#include <stdio.h>

#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#include "s2a.h"
#include "access.h"
#include "mem_alloc.h"

static void * _mysql_connect(const char * name)
{
    char dbip[256] = {0};
    char usr[256] = {0};
    char password[256] =  {0};
    char database[256] = {0};
    int port = 3306;
    char charset[256] = {0};

    MYSQL * mysql = get_mem(sizeof(MYSQL));
    if (mysql == 0) {
        printf("get_mem(sizeof(MYSQL)) failed: %s\n", strerror(errno));
        return 0;
    }

    if (mysql_init(mysql) == 0) {
        printf("mysql_init failed:[%d] %s\n", 
                mysql_errno(mysql), mysql_error(mysql));
        release_mem(mysql, sizeof(MYSQL));
        return 0;
    }

    if (mysql_real_connect(mysql, dbip, 
                usr, password,
                database, port, NULL, 0) == 0)
    {
        printf("mysql_real_connect failed:[%d] %s\n", 
                mysql_errno(mysql), mysql_error(mysql));
 
        mysql_close(mysql);
        release_mem(mysql, sizeof(MYSQL));
        return 0;
    }

    if (mysql_set_character_set(mysql, charset) != 0) {
        printf("mysql_set_character_set failed:[%d] %s\n", 
                mysql_errno(mysql), mysql_error(mysql));
 
        mysql_close(mysql);
        release_mem(mysql, sizeof(MYSQL));
        return 0;
    }
    return mysql;
}

static void _mysql_close(void * conn)
{
    MYSQL * mysql = (MYSQL*)conn;
    mysql_close(mysql);
    release_mem(mysql, sizeof(MYSQL));
}

static int setnblock(int socket)
{
	int flags;
	flags = fcntl(socket, F_GETFL, 0);
	if (flags == -1) {
		return -1;
	}

	flags |= O_NONBLOCK;
	flags |= O_NDELAY;
	if (fcntl(socket, F_SETFL, flags) == -1) {
		return -1;
	}
	return 0;
}
////////////////////////////////////////////////////////////////////////////////
// test

typedef struct Request
{
    time_t t;
    char buff[0];
}Request;

static int t_work(const char * request, int request_len,
        char ** respond, int * respond_len, void * p)
{
    static Access * mysql_access = 0;
    if (mysql_access == 0) {
        mysql_access = access_create(_mysql_connect, _mysql_close, 3);
    }

    const char * name = "aaa";
    const char * sql = "select * from foo";
    int len = strlen(sql);

    MYSQL * mysql = get_connection(mysql_access, name);
    if (mysql == 0) {
        return -1;
    }

    if (mysql_real_query(mysql, sql, len) != 0) {
        printf("mysql_real_query failed:[%d]%s\n", 
                mysql_errno(mysql), mysql_error(mysql));
        close_connection(mysql_access, name, mysql);
        return -1;
    }

    MYSQL_RES* res =  mysql_store_result(mysql);
    if (res == NULL) {
        int err = mysql_errno(mysql);
        if (err != 0) {
            printf("mysql_store_result failed:[%d]%s\n", 
                    err, mysql_error(mysql));
            close_connection(mysql_access, name, mysql);
            return -1;
        } 
    }

    //TODO;set res

    release_connection(mysql_access, "aaa", mysql);
    return 0;
}

uint64_t ileft_cnt = 0;
int g_exit = 0;

static void * read_thread(void * p)
{
    Worker * worker = (Worker*)p;

    setnblock(worker->fd);

    struct epoll_event event;
    event.events = EPOLLOUT|EPOLLIN;
    event.data.ptr = worker;

    int epoll_fd = epoll_create(1);
    if (epoll_fd < 0) {
        printf("epoll_create failed\n");
    }

    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, worker->fd, &event) != 0) {
        printf("epoll_ctl failed: %s\n", strerror(errno));
    }

    while (!g_exit) {
        struct epoll_event e[10];
        int fds = epoll_wait(epoll_fd, e, 10, 1000);
        if (fds < 0) {
            printf("epoll_wait failed: %s\n", strerror(errno));
        }

        if (fds == 0) {
            continue;
        }

        int i = 0;
        for (i = 0; i < fds; i++) {
            Worker * w = (Worker*)e[i].data.ptr;
            if (w == 0) {
                printf("error epoll_event.data.ptr == 0\n");
                continue;
            }

            uint64_t sn;
            int result;
            const char * request = 0;
            int request_len = 0;
            char * respond = 0;
            int respond_len = 0;
            while (get_respond(w, &sn, &result,
                        &request, &request_len,
                        &respond, &respond_len)) {

                Request * req = (Request*)request;

                int delay = time(0) - req->t;
                printf("respond: sn=%llu, delay = %d, result=%d [%s]\n",
                        (unsigned long long)sn, delay, result, respond?respond:"");

                unsigned int mem_size = 0;
                size_t ui_size = sizeof(unsigned int);

                memcpy(&mem_size, request-ui_size, ui_size);

                if (request) release_mem((void*)request, mem_size);
                //free(respond);
                ileft_cnt--;
            }
        }
    }
    printf("read_thread exit\n");
    return 0;
}

int main(int argc, char * argv[])
{
    if (!worker_init()) {
        printf("worker_init failed\n");
        return -1;
    }

    int thread_cnt = 3;
    if (argc >= 2) {
        thread_cnt = atoi(argv[1]);
    }

    Worker * worker = worker_create(t_work, thread_cnt, 0);
    if (worker == 0) {
        printf("create_worker failed\n");
        return 0;
    }

    uint64_t sn = 0;

    pthread_t read_thread_id;

    pthread_create(&read_thread_id, 0, read_thread, worker);

    while (!feof(stdin)) {
        size_t n = 1024;
        unsigned int mem_size = n + sizeof(Request) + sizeof(unsigned int);
        char * mem = (char*)get_mem(mem_size);
        if (mem==0) {
            printf("get_mem of request failed:%s\n", strerror(errno));
            break;
        }
        memcpy(mem, &mem_size, sizeof(unsigned int)); 

        Request  * req = (Request*)(mem + sizeof(unsigned int));

        if (fgets(req->buff, n, stdin) == 0 ) {
            continue;
        }

        int len = strlen(req->buff);
        if (len > 0 && req->buff[len-1] == '\n') {
            req->buff[len-1] = 0;
            len -= 0;
        }

        printf("request: sn=%llu [%s]\n", (unsigned long long)sn, req->buff);

        time(&req->t);

        if (!post_request(worker, sn++, 5, (char*)req, sizeof(Request)+len+1)) {
            release_mem(mem, mem_size);
            printf("post_request failed\n");
            continue;
        }
        ileft_cnt ++;
    }

    while (ileft_cnt > 0) {
        sleep(1);
    }

    g_exit = 1;

    void * p = 0;
    pthread_join(read_thread_id, &p);

    worker_destory(worker);
    return 0;
}
